

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/err.h>
#include "kfifo.h"
//#include <linux/log2.h>


int is_power_of_2(unsigned long n)

{
     return (n!=0 && ((n&(n-1)) == 0));
     
}


struct kfifo *kfifo_init(unsigned char *buffer, unsigned int size,
			 int gfp_mask, spinlock_t *lock)
{
	struct kfifo *fifo;

	/* size must be a power of 2 */
	BUG_ON(!is_power_of_2(size));

	fifo = kmalloc(sizeof(struct kfifo), gfp_mask);
	if (!fifo)
		return ERR_PTR(-ENOMEM);

	fifo->buffer = buffer;
	fifo->size = size;
	fifo->in = fifo->out = 0;
	fifo->lock = lock;

	return fifo;
}

struct kfifo *kfifo_alloc(unsigned int size, int gfp_mask, spinlock_t *lock)
{
	unsigned char *buffer;
	struct kfifo *ret;

	/*
	 * round up to the next power of 2, since our 'let the indices
	 * wrap' tachnique works only in this case.
	 */
	if (size & (size - 1)) {
		BUG_ON(size > 0x80000000);
		size = roundup_pow_of_two(size);
	}

	buffer = kmalloc(size, gfp_mask);
	if (!buffer)
		return ERR_PTR(-ENOMEM);

	ret = kfifo_init(buffer, size, gfp_mask, lock);

	if (IS_ERR(ret))
		kfree(buffer);

	return ret;
}

void kfifo_free(struct kfifo *fifo)
{
	kfree(fifo->buffer);
	kfree(fifo);
}

unsigned int kfifo_put(struct kfifo *fifo,
			 unsigned char *buffer, unsigned int len)
{
	unsigned int l;
       //rby
        unsigned long flags;
	unsigned int ret;

	spin_lock_irqsave(fifo->lock, flags);
       //end
	len = min(len, fifo->size - fifo->in + fifo->out);//可以放数据的长度

	/*
	 * Ensure that we sample the fifo->out index -before- we
	 * start putting bytes into the kfifo.
	 */
        
	smp_mb();

	/* first put the data starting from fifo->in to buffer end */
	//如果数据过长就先放知道尾部，然后在把其他的放到开头
	l = min(len, fifo->size - (fifo->in & (fifo->size - 1)));
	memcpy(fifo->buffer + (fifo->in & (fifo->size - 1)), buffer, l);

	/* then put the rest (if any) at the beginning of the buffer */
	memcpy(fifo->buffer, buffer + l, len - l);

	/*
	 * Ensure that we add the bytes to the kfifo -before-
	 * we update the fifo->in index.
	 */

	smp_wmb();

	fifo->in += len;

	//rby
	spin_unlock_irqrestore(fifo->lock, flags);
       //end rby
	return len;
}

unsigned int kfifo_get(struct kfifo *fifo,
			 unsigned char *buffer, unsigned int len)
{

	unsigned int l;
        //rby
       unsigned long flags;
	unsigned int ret;

	spin_lock_irqsave(fifo->lock, flags);
        //endrby
	len = min(len, fifo->in - fifo->out);

	/*
	 * Ensure that we sample the fifo->in index -before- we
	 * start removing bytes from the kfifo.
	 */

	smp_rmb();

	/* first get the data from fifo->out until the end of the buffer */
	l = min(len, fifo->size - (fifo->out & (fifo->size - 1)));
	memcpy(buffer, fifo->buffer + (fifo->out & (fifo->size - 1)), l);

	/* then get the rest (if any) from the beginning of the buffer */
	memcpy(buffer + l, fifo->buffer, len - l);

	/*
	 * Ensure that we remove the bytes from the kfifo -before-
	 * we update the fifo->out index.
	 */

	smp_mb();

	fifo->out += len;
        //rby
	if (fifo->in == fifo->out)
		fifo->in = fifo->out = 0;

	spin_unlock_irqrestore(fifo->lock, flags);
         //end rby
	return len;
}



unsigned int kfifo_len(struct kfifo *fifo)
{
	unsigned long flags;
	unsigned int ret;

	spin_lock_irqsave(fifo->lock, flags);

	ret = fifo->in - fifo->out;

	spin_unlock_irqrestore(fifo->lock, flags);

	return ret;
}
 void kfifo_reset(struct kfifo *fifo)

{
	unsigned long flags;

	spin_lock_irqsave(fifo->lock, flags);

	fifo->in = fifo->out = 0;

	spin_unlock_irqrestore(fifo->lock, flags);
}
